Swiftでコレクションを扱うためのOSS: TraverSwift
をつくりました。
主にScalaのTraversableトレイトやHaskellのData.Listにあるような一連の関数をSwiftで使ってみようという試みになっています。
入れ方
iOS 8 以上対象のプロジェクトでSwiftに対応したCocoapods0.36beta1を用いてインストールできます。
pod 'TraverSwift'
使い方
使い方についてはドキュメントページにも解説がありますが、こちらにも記載します。
以下各節は第一引数にどのようなプロトコルに準拠した型やストラクチャをとるかで区分けしています。
SequenceTypeプロトコル全般
any関数, all関数
any関数は少なくともひとつの要素が第二引数の条件式を満たす時にtrueを、all関数はすべての要素が第二引数の条件式を満たす時にtrueを返します。
// 少なくともひとつの要素が条件を満たす let seq = [1,2,3,4,5,6,7] any (seq) { a in a > 6 } // true // すべての要素が条件を満たす all (seq) { a in a > 0 } // true
intersperse関数
intersperse関数はすべての要素の間に新しく別の要素を入れて新たな配列を生成します。
// すべての要素の間に要素を新たに追加した配列を取得する let str = "abcde" let result = intersperse(str, ",") result == ["a",",","b",",","c",",","d",",","e"] // true
subsequence関数
部分列全般を配列として返します。
// 要素の部分列全般を配列として返す let seq = [1,2,3] let result = subsequences(seq) result == [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]] // true
scan関数
reduce関数の途中経過を配列として返します。
// reduceまでの過程を配列として返す let seq1 = SequenceOf([4, 2, 4]) scan(seq1, 64, /) == [64, 16, 8, 2] // true scan(seq2, 4) { x, y in 2 * x + y } == [4, 9, 20, 43] // true
flatMap関数
各要素からSequenceTypeを返すような関数を第二引数にとり、各要素に適用した結果を結合して配列として返します。
// 各要素への第二引数の適用結果を結合した配列として返す let seq1 = SequenceOf([4, 2, 3]) let result = flatMap(seq1) { elem in SequenceOf([elem, elem * 10 + elem]) } result == [4,44,2,22,3,33] // true
groupBy関数
第二引数に定義した条件式をもとにグループ分けして新しい配列を返します。
// 第二引数をもとにグループ分けされた配列を生成 let seq1 = SequenceOf([1,2,3,4,5,6,7,8,9]) let result1 = groupBy(seq1, <) result1 == [[1,2,3,4,5,6,7,8,9]] // true let result2 = groupBy(seq1, >) result2 == [[1],[2],[3],[4],[5],[6],[7],[8],[9]] // true let seq2 = SequenceOf([5,6,1,3,4,5,6,7,8,9,10]) let result3 = groupBy(seq2, <) result3 == [[5,6], [1,3,4,5,6,7,8,9,10]] // true let result4 = groupBy(seq2, >) result4 == [[5],[6,1],[3],[4],[5],[6],[7],[8],[9],[10]] // true
tail関数 & rtail関数
tail関数は先頭要素を、rtail(Scala, Haskellのinit)は最後の要素を引数から除外した新たな配列を生成します。
// 先頭要素の除外 let seq1 = SequenceOf([1, 2, 3, 4, 5, 6]) tail(seq1) == [2, 3, 4, 5, 6] // true // 最後の要素の除外 rtail(seq1) == [1, 2, 3, 4, 5] // true
tails関数 & rtails関数
tails関数はtail関数を引数に順次適用していった結果の配列を返します。 rtails関数はrtail関数を引数に順次適用していった結果の配列を返します。(順番は後ろの方が長い)
// tail関数を順次適用した結果の配列 let seq1 = SequenceOf([1, 2, 3, 4]) tails(seq1) == [[1,2,3,4],[2,3,4],[3,4],[4],[]] // true // rtail関数を順次適用した結果の配列 rtails(seq1) == [[],[1],[1,2],[1,2,3],[1,2,3,4]] // true
takeWhile関数 & dropWhile関数 & span関数
takeWhile関数は先頭から各要素に対して第二引数の条件を調べていき、いったんfalseになったらそこまでの要素を配列として返します。
dropWhile関数はtakeWhileで得られる結果を差し引いたものを配列として返します。
span関数はtakeWhile, dropWhileの結果をタプルで返します。
// 条件が当てはまる最大の先頭配列 let seq1 = [1,2,3,4,1,2,3,4] takeWhile(seq1) { elem in elem < 3 } == [1,2] // true // takeWhileで得られる結果を除いた残りの配列 dropWhile(seq1) { elem in elem < 3 } == [3,4,1,2,3,4] // true // takeWhile, dropWhileの結果をタプルで得る let result1 = span(seq1) { elem in elem < 3 } result1.0 == [1, 2] // true result1.1 == [3,4,1,2,3,4] // true
cast関数
暗黙的アンラップ型にくるまれた配列に対しては配列に対するasキャストが失敗する場合があるため、それを補う為の関数です。 第二引数には要素のキャスト先の型を渡します。
// 型が混合した配列についてはキャストが失敗し、Optional.Noneを返す let objs: [NSObject]! = [NSString(string: "aaa"), NSNumber(int: 123), NSString(string: "ag")] cast(objs, NSString.self) == nil // true // 型が混合していない配列についてはキャストが成功し、Optional.Someを返す let strs: [NSObject]! = [NSString(string: "aaa"), NSString(), NSString(string: "ag")] cast(strs, NSString.self) != nil // true
Equatable プロトコルに準拠した型の要素を持った SequenceTypeプロトコル
group関数
先頭から走査した各要素を等値演算子が真であるかどうかでグループ分けした配列を返します。
// == によるグループ分けを行った配列を返す let seq1 = SequenceOf([1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7]) let result1 = group(seq1) result1 == [[1,1,1,1],[2,2,2,2],[3,3],[2,2,2],[5],[6],[7]] // true
stripPrefix関数
第二引数のSequenceTypeを第一引数の先頭と比較し、合致していたら第二引数を除外した配列をOptional.Someにくるんで生成、合致していなかったらOptional.Noneを返します。
// 合致していたら第二引数を除外した配列をOptional.Someにくるんで生成 stripPrefix("foobar", "foo")! == ["b","a","r"] // true stripPrefix("foo", "foo")! == [] // true // 合致していなかったらOptional.Noneを返す stripPrefix("barfoo", "foo") == nil // true stripPrefix("barfoobaz", "foo") == nil // true
distinct関数
SequenceTypeから重複要素を除外して新たな配列を返します。
// 重複要素の除外された新たな配列を生成 distinct([1,2,3,4,5,4,3,2,3,1,0]) == [1,2,3,4,5,0] // true distinct("bannana") == ["b", "a", "n"] // true
union関数
2つの引数に対して和集合をとります。各引数に重複があった時は除外されます。
// 和集合の配列を生成 union([1,2,3], [2,3,4]) == [1,2,3,4] // true
intersect関数
2つの引数に対して積集合をとりますが、一つ目の引数の重複は除外されません。
// 積集合の配列を得る intersect([1,2,3,4], [2,4,6,8]) == [2,4] // true // 一つ目の重複要素は除外されない intersect([1,2,2,3,4], [6,4,4,2]) == [2,2,4] // true
equel演算子
2つの引数の各要素が等しく、かつ並び順も等しい場合にtrueを返す演算子です。
// 各要素、並び順の等値性判定 let seq1 = SequenceOf([1,2,3,4,5,6]) let seq2 = SequenceOf([1,2,3,4,5,6]) seq1 == seq2 // true
特殊な型を持った SequenceTypeプロトコル
sum関数 & product関数
IntegerArithmeticType, FloatingArithmeticType(Double, Float)の要素を持つSequenceTypeについて、各要素の総和、総積を計算します。
// 総和 let seq1 = SequenceOf([1,2,3,4,5,6]) sum(seq1) == 21 // true let seq2 = SequenceOf([1.1,2.2,3.3,4.0,5.2,6.1]) sum(seq2) == 21.9 // true // 総積 let seq3 = SequenceOf([1.0,2.2,3.0,4.1,5.0,6.0]) product(seq1) == 720 // true product(seq3) == 1.0 * 2.2 * 3.0 * 4.1 * 5.0 * 6.0 // true
and関数 & or関数
BooleanTypeの要素を持つSequenceTypeについて and関数はすべてがtrueかどうかを判定し、or関数は少なくともひとつがtrueかどうかを判定します。
// すべての要素がtrueかどうか let seq1 = SequenceOf([true, true, true, true]) and(seq1) // true // 少なくともひとつの要素がtrueかどうか let seq2 = SequenceOf([false, true, false, false]) or(seq2) // true
CollectionTypeプロトコル
findIndex関数
引数を走査していき、要素に対する条件式が真である最初のインデックスをOptionalにくるんで返します。条件式が真であるような要素が見つからなかった場合はOptional.Noneを返します。
// 要素に対する条件式が真であるような最初のインデックスを得る let col1 = [1,2,3,4,4,6] findIndex(col1) { elem in elem > 3 }! == 3 // true // 条件式が真であるような要素が見つからない場合はOptional.Noneを得る findIndex(col1) { elem in elem > 6 } == nil // true
take関数 & drop関数 & split関数
take関数は最初から指定インデックス番目(指定インデックスの要素自体は含まない)までの配列を取得します。
drop関数は指定インデックス番目から最後まで配列を取得します。
splitAt関数はtake, drop関数の結果を一緒にタプルとして取得します。
// 最初から指定インデックス番目までの配列を取得 let col1 = [1,2,3,4,5,6,7] take(col1, 3) == [1, 2, 3] // true take(col1, 7) == [1, 2, 3, 4, 5, 6, 7] // true // 指定インデックス番目から最後までの配列を取得 drop(col1, 3) == [4, 5, 6, 7] // true drop(col1, 0) == [1, 2, 3, 4, 5, 6, 7] // true // take, dropの結果をタプルで取得 let result1 = splitAt(col1, 3) result1.0 == [1, 2, 3] // true result1.1 == [4, 5, 6, 7] // true
Arrayストラクチャ
existsAny関数 & existsAll関数
各要素がオプショナルであるような配列に対して、少なくともひとつの要素が.SomeであるかどうかをexistsAny関数は判定し、すべての要素が.SomeであるかどうかをexistsAll関数は判定します。
現在はコンパイラクラッシュの為、T?型を要素に持つSequenceTypeプロトコルを第一引数に持つ関数にすることを断念しています。
// 少なくともひとつの要素が.Someであるかどうか let arr1 = ["1e3","123","rf3","rf3"].map{ str in str.toInt() } existsAny(arr1) // true // すべての要素が.Someであるかどうか let arr2 = ["13","123","3","312"].map{ str in str.toInt() } existsAll(arr2) // true
concat関数
配列の配列を結合し、配列として返します。
現在はコンパイラクラッシュの為、SequenceTypeを要素に持つSequenceTypeを第一引数に持つ関数にすることを断念しています。
// 配列の結合 let arr1 = [[1,2,3],[4,5,6],[7,8,9]] concat(arr1) == [1,2,3,4,5,6,7,8,9] // true
ライセンス
MITです